package com.yaukie.socket;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.yaukie.socket.constants.Constants;
import com.yaukie.socket.dto.MessageDTO;
import com.yaukie.socket.dto.UserDTO;
import com.yaukie.socket.manager.WsSessionManger;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.ExceptionUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.nio.charset.Charset;
import java.security.SecureRandom;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

@Component
@ServerEndpoint("/websocket")
@Slf4j
public class WebSocketServer implements ApplicationContextAware {


    private  static   WsSessionManger wsSessionManger ;

    private static ApplicationContext applicationContext ;

    private static Map<String,UserDTO> USER_CACHE = new ConcurrentHashMap() ;

    /**
     * @Description： 连接建立的时候 要做的事情
     * @author : yuenbin
     * @date： 16:22 2024/11/26
     * @Motto： It is better to be clear than to be clever !
     **/
    @OnOpen
    public void onOpen(Session session){
        // 获取参数
        UserDTO userDTO = null  ;
        Map<String, List<String>> requestParameterMap = session.getRequestParameterMap();
        if(!CollectionUtils.isEmpty(requestParameterMap)){
            List<String> nickNames = requestParameterMap.get("nickName");
            List<String> chatRoomIds = requestParameterMap.get("chatRoomId");
            if(!CollectionUtils.isEmpty(nickNames) &&
            !CollectionUtils.isEmpty(chatRoomIds)){
                String nickName = nickNames.get(0);
                String chatRoomId = chatRoomIds.get(0);
                log.info("用户{}上线了,使用的是{}房间",nickName,chatRoomId);
                // 构建用户信息
                if(!USER_CACHE.containsKey(nickName)){
                    userDTO = new UserDTO() ;
                    userDTO.setUserId(UUID.randomUUID().toString());
                    userDTO.setNickName(nickName);
                    userDTO.setChatRoomId(chatRoomId);
                    userDTO.setHeadPic((new SecureRandom().nextInt(10)+1)+"");
                    USER_CACHE.put(nickName,userDTO) ;
                }else {
                      userDTO = USER_CACHE.get(nickName);
                }

                // 添加上线用户
                wsSessionManger.addSession(userDTO,session);
                // 发送系统通知
                MessageDTO messageDTO = new MessageDTO() ;
                messageDTO.setChannel(chatRoomId);
                messageDTO.setFrom("系统消息");
                messageDTO.setTo("当前会话");
                // 需要统计当前通道下的总人数
                messageDTO.setContent(userDTO.getNickName() + " 上线了,当前在线总人数为："+ wsSessionManger.getOnlineUsers().stream()
                        .filter(u -> u.getChatRoomId()
                                .equalsIgnoreCase(chatRoomId))
                        .collect(Collectors.toList()).size());
                messageDTO.setMsgType(Constants.MsgType.SYS.getCode());
                messageDTO.setSendTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
//                sendSysMsg(session,messageDTO);
                // 发送广播通知
                sendBroadCastMsg(messageDTO) ;

            }
        }
    }

    /**
     * @Description： 收到消息之后的处理
     * @author : yuenbin
     * @date： 19:19 2024/11/26
     * @Motto： It is better to be clear than to be clever !
     **/
    @OnMessage
    public void onMessage(Session session,String message ){

        try {
            StringRedisTemplate redisTemplate = applicationContext.getBean(StringRedisTemplate.class) ;
            message = new String(message.getBytes(Charset.defaultCharset()), Charset.defaultCharset()) ;
            // 获取当前会话中的通道,并按此通道发送数据至Redis
            String channel = session.getRequestParameterMap().get("chatRoomId").get(0);
            redisTemplate.convertAndSend(channel,message);

        }catch (Exception ex )
        {
            log.error("消息转化出现异常，原因为：{}",ExceptionUtils.unwrapInvocationTargetException(ex));
        }

    }

    @OnClose
    public void onClose(Session session ){
        Map<String, List<String>> requestParameterMap = session.getRequestParameterMap();
        if(!CollectionUtils.isEmpty(requestParameterMap)){
            String nickName = requestParameterMap.get("nickName").get(0);
            if(!USER_CACHE.containsKey(nickName)){
                log.info("用户{}当前不在线",nickName);
                return;
            }
            UserDTO userDTO = USER_CACHE.get(nickName);
            USER_CACHE.remove(nickName)  ;
            // 下线操作
            wsSessionManger.removeSession(userDTO);
            // 进行系统消息发送提醒
            MessageDTO messageDTO = new MessageDTO() ;
            messageDTO.setChannel(userDTO.getChatRoomId());
            messageDTO.setFrom("系统消息");
            messageDTO.setTo("当前会话");
            messageDTO.setContent(userDTO.getNickName() + " 下线了,当前在线总人数为："+ wsSessionManger.getOnlineUsers().size());
            messageDTO.setMsgType(Constants.MsgType.SYS.getCode());
            messageDTO.setSendTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
//            sendSysMsg(session,messageDTO);
            // 进行广播通知
            sendBroadCastMsg(messageDTO);
        }
    }

    @OnError
    public void onError(Throwable ex ){
        log.error("连接 websocket 出现异常，原因为：{}",ExceptionUtils.unwrapInvocationTargetException(ex));
    }

    // 发送普通聊天消息
    public void sendMsg (MessageDTO messageDTO){
        String msg = JSON.toJSONString(messageDTO,SerializerFeature.WriteMapNullValue) ;
        String to = messageDTO.getTo();
        String from = messageDTO.getFrom();
        // 兼容群发情况
        if(to.equalsIgnoreCase("ALL")){
            sendBroadCastMsg(messageDTO);
        }else {

            if(USER_CACHE.containsKey(to)){
                UserDTO userDTO = USER_CACHE.get(to);
                if(wsSessionManger.getSessionPools().containsKey(userDTO)){
                    Session session = wsSessionManger.getSessionPools().get(userDTO);
                    sendMessage(session,msg);
                }
            }else {
                // 判断当前要聊天的用户是否在线，如果不在线那么就发送一条离线提醒
                Session session = wsSessionManger.getSessionPools().get(USER_CACHE.get(from));
                messageDTO.setContent("当前已离线");
                messageDTO.setFrom(to);
                messageDTO.setTo(from);
                sendSysMsg(session,messageDTO);
            }
        }
    }

    // 发送广播消息
    public void sendBroadCastMsg(MessageDTO messageDTO){
        if (!CollectionUtils.isEmpty(wsSessionManger.getOnlineUsers())){

            wsSessionManger.getOnlineUsers().stream().forEach(item ->{
                Session session = wsSessionManger.getSessionPools().get(item);
                sendMessage(session, JSON.toJSONString(messageDTO, SerializerFeature.WriteMapNullValue));
            });
        }
    }


    // 发送系统消息
    public void sendSysMsg(Session session,MessageDTO messageDTO){
        sendMessage(session,JSON.toJSONString(messageDTO,SerializerFeature.WriteMapNullValue));
    }

    private void sendMessage(Session session , String message ){
        try {
            if(!StringUtils.isEmpty(session)){
                synchronized (session){
                    session.getBasicRemote().sendText(message);
                    log.info("消息发送成功，发送内容为：{}",message);
                }
            }
        } catch (IOException e) {
         log.error("消息发送异常，原因为：{}", ExceptionUtils.unwrapInvocationTargetException(e));
        }
    }


    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        WebSocketServer.applicationContext = applicationContext ;
        WebSocketServer.wsSessionManger = applicationContext.getBean(WsSessionManger.class) ;
    }

}
